package cn.easyutil.easyproject.easySpringboot.handler;

import cn.easyutil.easyproject.easySpringboot.context.Context;
import cn.easyutil.easyproject.easySpringboot.util.RequestPool;
import com.example.easyJavaUtil.JsonUtil;
import com.example.easyJavaUtil.LoggerUtil;
import com.example.easyJavaUtil.StringUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import cn.easyutil.easyproject.easySpringboot.context.Error;
import cn.easyutil.easyproject.easySpringboot.exception.CommonException;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.lang.Nullable;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;

/**
 * 请求为json格式的body体的时候进行解析(form表单形式的不走此方法)
 * 此处可集成加解密
 * @author spc
 *
 */
@Configuration
public class JsonRequestAuthorition extends AbstractJackson2HttpMessageConverter {
	
	/**
	 * type:请求controller中的参数类型
	 * contextClass:请求的controller类
	 * inputMessage:请求的body体
	 */
    @Override
    public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        ObjectMapper objectMapper = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        this.objectMapper = objectMapper;

        HttpServletRequest request = RequestPool.get();

        String requestUri = request.getRequestURI();
        String callNoStr = request.getRequestURI().substring(requestUri.lastIndexOf("/") + 1);
        // 请求参数
        Object result = null;
        // 是否加解密
        if(!RequestPool.enableAuth()){
        	result = super.read(type, contextClass, inputMessage);
            if(RequestPool.getUserNotException() != null){
            	LoggerUtil.info(this.getClass(),"[" + callNoStr + "]请求用户id为:" + RequestPool.getUserId());
            }else{
            	LoggerUtil.info(this.getClass(),"[" + callNoStr + "]请求用户id为:null");
            }
            LoggerUtil.info(this.getClass(),"[" + callNoStr + "]请求参数为:" + JsonUtil.beanToJson(result));
            RequestPool.setSessionAttribute(Context.threadLocal_request_url, callNoStr);
            return result;
        }
        //加密的情况下，指定接口不加密
        if (callNoStr.length() < 5) {
        	return result;
        }
        //解密处理
        Long userId = RequestPool.getUserId();
        //获取用户的解密token
        String token = RequestPool.getToken();
        //读取用户的请求数据
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int i;
        while ((i = inputMessage.getBody().read()) != -1) {
            baos.write(i);
        }
        String text = baos.toString();
        LoggerUtil.info(this.getClass(),"[" + callNoStr + "]请求用户id为:" + userId);
        LoggerUtil.info(this.getClass(),"[" + callNoStr + "]请求密文为:" + text);
        //对数据进行解密
        try {
        	text = StringUtil.AESDecode(text, token);
		} catch (Exception e) {
			throw new CommonException(Error.biz_nottoken);
		}
        LoggerUtil.info(this.getClass(),"[" + callNoStr + "]解密后的参数为:" + text);
        RequestPool.setSessionAttribute(Context.threadLocal_request_url, callNoStr);
        //获取实际的controller参数类型
        JavaType javaType = getJavaType(type, contextClass);
        return JsonUtil.jsonToBean(text, javaType.getRawClass());

    }

    @Override
    protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        this.objectMapper = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

        HttpServletRequest request = RequestPool.get();

        String requestUri = request.getRequestURI();
        String callNoStr = request.getRequestURI().substring(requestUri.lastIndexOf("/") + 1);

        // 如果不是api定义的前缀 走不加密
        if (callNoStr.length() < 5) {
            try {
                super.writeInternal(object, type, outputMessage);
            } catch (Exception e) {
                throw new CommonException(Error.system_error);
            }
        } else {
            super.writeInternal(object, type, outputMessage);
        }

    }

    @Nullable
    private String jsonPrefix;

    /**
     * Construct a new
     * {@link org.springframework.http.converter.json.MappingJackson2HttpMessageConverter}
     * using default configuration provided by
     * {@link Jackson2ObjectMapperBuilder}.
     */
    public JsonRequestAuthorition() {
        this(Jackson2ObjectMapperBuilder.json().build());
    }

    /**
     * Construct a new
     * {@link org.springframework.http.converter.json.MappingJackson2HttpMessageConverter}
     * with a custom {@link ObjectMapper}. You can use
     * {@link Jackson2ObjectMapperBuilder} to build it easily.
     *
     * @see Jackson2ObjectMapperBuilder#json()
     */
    public JsonRequestAuthorition(ObjectMapper objectMapper) {
        super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
    }

    /**
     * Specify a custom prefix to use for this view's JSON output. Default is
     * none.
     *
     * @see #setPrefixJson
     */
    public void setJsonPrefix(String jsonPrefix) {
        this.jsonPrefix = jsonPrefix;
    }

    /**
     * Indicate whether the JSON output by this view should be prefixed with
     * ")]}', ". Default is false.
     * <p>
     * Prefixing the JSON string in this manner is used to help prevent JSON
     * Hijacking. The prefix renders the string syntactically invalid as a
     * script so that it cannot be hijacked. This prefix should be stripped
     * before parsing the string as JSON.
     *
     * @see #setJsonPrefix
     */
    public void setPrefixJson(boolean prefixJson) {
        this.jsonPrefix = (prefixJson ? ")]}', " : null);
    }

    @Override
    protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
        if (this.jsonPrefix != null) {
            generator.writeRaw(this.jsonPrefix);
        }
    }

}
